package com.example.fastexcel.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.UUID;

/**
 * 文件上传、下载及 Excel 导入导出控制器
 * 使用 Spring Boot 3 + OpenAPI 3 标准实现
 */
@RestController
@RequestMapping("/api/files")
@Tag(name = "文件管理", description = "提供文件上传、下载、Excel导入导出功能")
public class ExcelFileController {

    // 文件存储根目录（生产环境建议使用独立存储如 MinIO、OSS）
    private static final String UPLOAD_DIR = "uploads/";

    static {
        // 确保上传目录存在
        Path path = Paths.get(UPLOAD_DIR);
        try {
            if (!Files.exists(path)) {
                Files.createDirectories(path);
            }
        } catch (IOException e) {
            throw new RuntimeException("初始化上传目录失败", e);
        }
    }

    /**
     * 上传单个文件（支持任意格式） https://docs.springframework.org.cn/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/multipart-forms.html
     *
     * @param file 待上传的文件（前端通过 form-data 传入）
     * @return 上传成功后的文件信息
     */
    @PostMapping("/upload")
    @Operation(summary = "上传单个文件", description = "支持任意类型文件，如 PDF、图片、Excel 等")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "上传成功", content = @Content(schema = @Schema(implementation = FileResponse.class))),
        @ApiResponse(responseCode = "400", description = "文件为空或格式错误"),
        @ApiResponse(responseCode = "500", description = "服务器内部错误")
    })
    public ResponseEntity<FileResponse> uploadFile(@RequestPart("file") MultipartFile file) {
        // 1. 校验文件是否为空
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().body(new FileResponse("文件不能为空"));
        }

        // 2. 获取原始文件名
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || originalFilename.trim().isEmpty()) {
            return ResponseEntity.badRequest().body(new FileResponse("文件名无效"));
        }

        // 3. 生成唯一文件名（避免重名和安全问题）
        String fileExtension = FilenameUtils.getExtension(originalFilename); // 获取扩展名
        String fileName = UUID.randomUUID().toString() + "." + fileExtension;

        // 4. 构建保存路径
        Path filePath = Paths.get(UPLOAD_DIR + fileName);

        try {
            // 5. 将文件写入磁盘
            Files.write(filePath, file.getBytes());

            // 6. 返回成功响应
            FileResponse response = new FileResponse(
                    fileName,
                    originalFilename,
                    file.getSize(),
                    "/api/files/download/" + fileName
            );
            return ResponseEntity.ok(response);

        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new FileResponse("文件保存失败: " + e.getMessage()));
        }
    }

    /**
     * 下载文件（根据文件名）
     *
     * @param fileName 文件在服务器上的唯一名称
     * @param response HTTP 响应对象（用于设置下载头）
     */
    @GetMapping("/download/{fileName}")
    @Operation(summary = "下载文件", description = "根据服务器存储的唯一文件名下载文件")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "文件下载成功", content = @Content(mediaType = "application/octet-stream")),
        @ApiResponse(responseCode = "404", description = "文件不存在"),
        @ApiResponse(responseCode = "500", description = "服务器读取文件失败")
    })
    public void downloadFile(@PathVariable String fileName, HttpServletResponse response) {
        Path filePath = Paths.get(UPLOAD_DIR + fileName);

        try {
            // 1. 检查文件是否存在
            if (!Files.exists(filePath)) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
                return;
            }

            // 2. 获取原始文件名（需从数据库或缓存中获取，此处简化为保留原名）
            // 实际项目中应存储原始文件名与唯一文件名的映射关系
            String originalName = fileName.contains(".") ? fileName.substring(0, fileName.lastIndexOf(".")) : fileName;
            String encodedName = URLEncoder.encode(originalName, "UTF-8");

            // 3. 设置响应头：触发浏览器下载
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedName + "\"");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

            // 4. 将文件内容写入响应流
            try (InputStream inputStream = Files.newInputStream(filePath);
                 OutputStream outputStream = response.getOutputStream()) {

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
                outputStream.flush();
            }

        } catch (IOException e) {
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "下载失败: " + e.getMessage());
            } catch (IOException ex) {
                // 忽略二次异常
            }
        }
    }

    /**
     * 导出 Excel 文件（示例：导出用户数据）
     *
     * @param response HTTP 响应对象
     */
    @GetMapping("/export/excel")
    @Operation(summary = "导出 Excel 文件", description = "导出模拟的用户数据到 Excel (.xlsx)")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "Excel 文件导出成功", content = @Content(mediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")),
        @ApiResponse(responseCode = "500", description = "导出失败")
    })
    public void exportExcel(HttpServletResponse response) {
        try {
            // 1. 设置响应头：告诉浏览器这是一个 Excel 文件，强制下载
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = "用户数据_" + LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));

            // 2. 使用 Apache POI 创建 Excel 文件
            org.apache.poi.ss.usermodel.Workbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook();
            org.apache.poi.ss.usermodel.Sheet sheet = workbook.createSheet("用户数据");

            // 3. 创建表头
            org.apache.poi.ss.usermodel.Row headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("ID");
            headerRow.createCell(1).setCellValue("姓名");
            headerRow.createCell(2).setCellValue("邮箱");
            headerRow.createCell(3).setCellValue("注册时间");

            // 4. 模拟数据（实际应从数据库查询）
            String[][] data = {
                    {"1", "张三", "zhangsan@example.com", "2025-01-15"},
                    {"2", "李四", "lisi@example.com", "2025-02-20"},
                    {"3", "王五", "wangwu@example.com", "2025-03-10"}
            };

            for (int i = 0; i < data.length; i++) {
                org.apache.poi.ss.usermodel.Row row = sheet.createRow(i + 1);
                for (int j = 0; j < data[i].length; j++) {
                    row.createCell(j).setCellValue(data[i][j]);
                }
            }

            // 5. 自动调整列宽
            for (int i = 0; i < 4; i++) {
                sheet.autoSizeColumn(i);
            }

            // 6. 写入响应流
            workbook.write(response.getOutputStream());
            workbook.close();

        } catch (Exception e) {
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Excel 导出失败: " + e.getMessage());
            } catch (IOException ex) {
                // 忽略
            }
        }
    }

    /**
     * 导入 Excel 文件（解析用户数据）
     *
     * @param file Excel 文件（.xlsx）
     * @return 解析结果
     */
    @PostMapping("/import/excel")
    @Operation(summary = "导入 Excel 文件", description = "上传 Excel 文件并解析用户数据（仅支持 .xlsx）")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "导入成功", content = @Content(schema = @Schema(implementation = ImportResult.class))),
        @ApiResponse(responseCode = "400", description = "文件格式错误或数据缺失"),
        @ApiResponse(responseCode = "500", description = "解析失败")
    })
    public ResponseEntity<ImportResult> importExcel(@RequestPart("file") MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().body(new ImportResult(false, "文件为空"));
        }

        // 仅允许 .xlsx 格式
        if (!file.getOriginalFilename().endsWith(".xlsx")) {
            return ResponseEntity.badRequest().body(new ImportResult(false, "仅支持 .xlsx 格式"));
        }

        try (InputStream inputStream = file.getInputStream()) {
            org.apache.poi.ss.usermodel.Workbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook(inputStream);
            org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);

            int rowCount = sheet.getLastRowNum();
            if (rowCount < 1) {
                return ResponseEntity.badRequest().body(new ImportResult(false, "Excel 无有效数据行"));
            }

            int successCount = 0;
            StringBuilder errorMessages = new StringBuilder();

            // 跳过表头（第0行）
            for (int i = 1; i <= rowCount; i++) {
                org.apache.poi.ss.usermodel.Row row = sheet.getRow(i);
                if (row == null) continue;

                // 获取单元格值（注意空值处理）
                String id = getCellValue(row.getCell(0));
                String name = getCellValue(row.getCell(1));
                String email = getCellValue(row.getCell(2));
                String registerTime = getCellValue(row.getCell(3));

                // 简单校验
                if (name == null || name.trim().isEmpty() || email == null || !email.contains("@")) {
                    errorMessages.append("第").append(i + 1).append("行: 姓名或邮箱无效；");
                    continue;
                }

                // 此处可调用服务层保存到数据库
                successCount++;
            }

            workbook.close();

            return ResponseEntity.ok(new ImportResult(true, "导入成功", successCount, errorMessages.toString().trim()));

        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ImportResult(false, "Excel 解析异常: " + e.getMessage()));
        }
    }

    /**
     * 安全获取单元格值（处理 null 和不同类型）
     */
    private String getCellValue(org.apache.poi.ss.usermodel.Cell cell) {
        if (cell == null) return null;
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    return String.valueOf((long) cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }
}
